unit Behavior;

interface

uses Classes, Math, Mathematic, Vector, Obstacle, Path, uOSPRand;

type
  TBehavior = class;

  TBehaviorsList = class(TList)
  private
  public
    constructor Create;
    constructor CreateCopy(paBehaviorsList: TBehaviorsList);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;
  end;

  TBehavior = class
  private
  public
    constructor Create;
    constructor CreateCopy(paBehavior: TBehavior);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;virtual;abstract;
  end;

  TSeek = class(TBehavior)
  private
  public
    constructor Create;
    constructor CreateCopy(paSeek: TSeek);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
  end;

  TFlee = class(TBehavior)
  private
  public
    constructor Create;
    constructor CreateCopy(paFlee: TFlee);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
  end;

  TArrival = class(TBehavior)
  private
    aDecelerateDistance: TFloat;
  public
    constructor Create(paDecelerateDistance: TFloat);
    constructor CreateCopy(paArrival: TArrival);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
    property DecelerateDistance: TFloat read aDecelerateDistance write aDecelerateDistance;
  end;

  TSeparation = class(TBehavior)
  private
    aRadius: TFloat;
  public
    constructor Create(paRadius: TFloat);
    constructor CreateCopy(paSeparation: TSeparation);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
    property Radius: TFloat read aRadius write aRadius;
  end;

  TWander = class(TBehavior)
  private
    aRadius: TFloat;
  public
    constructor Create(paRadius: TFloat);
    constructor CreateCopy(paWander: TWander);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
    property Radius: TFloat read aRadius write aRadius;
  end;

  TAlignment = class(TBehavior)
  private
    aRadius: TFloat;
  public
    constructor Create(paRadius: TFloat);
    constructor CreateCopy(paAlignment: TAlignment);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
    property Radius: TFloat read aRadius write aRadius;
  end;

  TCohesion = class(TBehavior)
  private
    aRadius: TFloat;
  public
    constructor Create(paRadius: TFloat);
    constructor CreateCopy(paCohesion: TCohesion);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
    property Radius: TFloat read aRadius write aRadius;
  end;

  TObstacleAvoidance = class(TBehavior)
  private
    aRadius: TFloat;
  public
    constructor Create(paRadius: TFloat);
    constructor CreateCopy(paObstacleAvoidance: TObstacleAvoidance);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
    property Radius: TFloat read aRadius write aRadius;
  end;

  TPathFollow = class(TBehavior)
  private
  public
    constructor Create;
    constructor CreateCopy(paPathFollow: TPathFollow);
    destructor Destroy;override;
    function Calculate(paPedestrian: Pointer): TVector;override;
  end;

implementation

uses Pedestrian;

constructor TBehaviorsList.Create;
begin
    inherited Create;
end;

constructor TBehaviorsList.CreateCopy(paBehaviorsList: TBehaviorsList);
begin
    inherited Create;
end;

destructor TBehaviorsList.Destroy;
begin
    inherited Destroy;
end;

function TBehaviorsList.Calculate(paPedestrian: Pointer): TVector;
var ResultVector, TempVector: TVector;
    I: Integer;
begin
    ResultVector := TVector.Create;
    for I := 0 to Count - 1 do begin
        TempVector := TBehavior(Items[I]).Calculate(paPedestrian);
        ResultVector.AddSelf(TempVector);
        TempVector.Free;
    end;
    Result := ResultVector;
end;

constructor TBehavior.Create;
begin
    inherited Create;
end;

constructor TBehavior.CreateCopy(paBehavior: TBehavior);
begin
    inherited Create;
end;

destructor TBehavior.Destroy;
begin
    inherited Destroy;
end;

constructor TSeek.Create;
begin
    inherited Create;
end;

constructor TSeek.CreateCopy(paSeek: TSeek);
begin
    inherited CreateCopy(paSeek);
end;

destructor TSeek.Destroy;
begin
    inherited Destroy;
end;

function TSeek.Calculate(paPedestrian: Pointer): TVector;
var DesiredVelocity: TVector;
    Pedestrian: TPedestrianEditor;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    DesiredVelocity := Pedestrian.Destination.Subtract(Pedestrian.Position);
    DesiredVelocity.TruncateSelf(Pedestrian.MaxSpeed);
    DesiredVelocity.SubtractSelf(Pedestrian.Velocity);
    Result := DesiredVelocity;
end;

constructor TFlee.Create;
begin
    inherited Create;
end;

constructor TFlee.CreateCopy(paFlee: TFlee);
begin
    inherited CreateCopy(paFlee);
end;

destructor TFlee.Destroy;
begin
    inherited Destroy;
end;

function TFlee.Calculate(paPedestrian: Pointer): TVector;
var DesiredVelocity: TVector;
    Pedestrian: TPedestrianEditor;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    DesiredVelocity := Pedestrian.Destination.Subtract(Pedestrian.Position);
    DesiredVelocity.TruncateSelf(Pedestrian.MaxSpeed);
    Result := Pedestrian.Velocity.Subtract(DesiredVelocity);
    DesiredVelocity.Free;
end;

constructor TArrival.Create(paDecelerateDistance: TFloat);
begin
    inherited Create;
    aDecelerateDistance := paDecelerateDistance;
end;

constructor TArrival.CreateCopy(paArrival: TArrival);
begin
    inherited CreateCopy(paArrival);
    aDecelerateDistance := paArrival.aDecelerateDistance;
end;

destructor TArrival.Destroy;
begin
    inherited Destroy;
end;

function TArrival.Calculate(paPedestrian: Pointer): TVector;
var DesiredVelocity: TVector;
    Distance, Speed: TFloat;
    Pedestrian: TPedestrianEditor;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    DesiredVelocity := Pedestrian.Destination.Subtract(Pedestrian.Position);
    Distance := DesiredVelocity.Length;
    Speed := Min(Distance / aDecelerateDistance, Pedestrian.MaxSpeed);
    DesiredVelocity.MultiplySelf(Speed / Distance);
    DesiredVelocity.SubtractSelf(Pedestrian.Velocity);
    DesiredVelocity.TruncateSelf(Pedestrian.MaxSpeed);
    Result := DesiredVelocity;
end;

constructor TSeparation.Create(paRadius: TFloat);
begin
    inherited Create;
    aRadius := paRadius;
end;

constructor TSeparation.CreateCopy(paSeparation: TSeparation);
begin
    inherited CreateCopy(paSeparation);
    aRadius := paSeparation.Radius;
end;

destructor TSeparation.Destroy;
begin
    inherited Destroy;
end;

function TSeparation.Calculate(paPedestrian: Pointer): TVector;
var Pedestrian, NearPedestrian: TPedestrianEditor;
    DesiredVelocity, NegateVector, Intrusion, LocalPosition: TVector;
    NearPedestriansList: TPedestriansList;
    I: Integer;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    DesiredVelocity := TVector.Create(0.0, 0.0, 0.0);
    NearPedestriansList := Pedestrian.GetNearPedestrians(aRadius / 2.0, true, 0.5);
    if NearPedestriansList.Count <> 0 then begin
        for I := 0 to NearPedestriansList.Count - 1 do begin
            NegateVector := Pedestrian.Position.Subtract(TPedestrianEditor(NearPedestriansList[I]).Position);
            DesiredVelocity.AddSelf(NegateVector.DivideSelf(Pedestrian.Position.Distance(TPedestrianEditor(NearPedestriansList[I]).Position)));
            NegateVector.Free;
        end;
    end;
    NearPedestriansList.Free;
    NearPedestrian := Pedestrian.GetNearPedestrian(aRadius, false, 0.5);
    if NearPedestrian <> nil then begin
        Intrusion := Pedestrian.GetIntersection(NearPedestrian.Position, NearPedestrian.Width, 1.5);
        if Intrusion.Length <> 0 then begin
            Intrusion.DivideSelf(Pedestrian.Position.Distance(NearPedestrian.Position));
            DesiredVelocity.AddSelf(Intrusion);
        end;
{
        LocalPosition := Pedestrian.LocalSpace.LocalizePosition(NearPedestrian.Position);
        if (LocalPosition.Y < 0) and (LocalPosition.X <= Pedestrian.Width / 2.0) and (LocalPosition.X >= -Pedestrian.Width / 2.0) then
                Pedestrian.Acceleration := 0.0
        else Pedestrian.Acceleration := 1.0;
        LocalPosition.Free;
}
        Intrusion.Free;
    end
    else Pedestrian.Acceleration := 1.0;
    Result := DesiredVelocity;
end;

constructor TWander.Create(paRadius: TFloat);
begin
    inherited Create;
    aRadius := paRadius;
end;

constructor TWander.CreateCopy(paWander: TWander);
begin
    inherited CreateCopy(paWander);
    aRadius := paWander.Radius;
end;

destructor TWander.Destroy;
begin
    inherited Destroy;
end;

function TWander.Calculate(paPedestrian: Pointer): TVector;
var Pedestrian: TPedestrianEditor;
    DesiredVelocity, LocalVector: TVector;
    Randomizer: tRndm;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    Randomizer := tRndm.Create(true);
    LocalVector := TVector.Create(Pedestrian.MaxSpeed, Randomizer.Rand * aRadius, 0.0);
    if Randomizer.Rand < 0.5 then LocalVector.Y := -LocalVector.Y;
    LocalVector.ScaleSelf(Pedestrian.MaxSpeed);
    DesiredVelocity := Pedestrian.LocalSpace.GlobalizeDirection(LocalVector);
    DesiredVelocity.AddSelf(Pedestrian.Velocity);
    DesiredVelocity.ScaleSelf(Pedestrian.MaxSpeed);
    LocalVector.Free;
    Randomizer.Free;
    Result := DesiredVelocity;
end;

constructor TAlignment.Create(paRadius: TFloat);
begin
    inherited Create;
    aRadius := paRadius;
end;

constructor TAlignment.CreateCopy(paAlignment: TAlignment);
begin
    inherited CreateCopy(paAlignment);
    aRadius := paAlignment.Radius;
end;

destructor TAlignment.Destroy;
begin
    inherited Destroy;
end;

function TAlignment.Calculate(paPedestrian: Pointer): TVector;
var Pedestrian: TPedestrianEditor;
    DesiredVelocity, Direction, LocalDirection: TVector;
    NearPedestriansList: TPedestriansList;
    I: Integer;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    DesiredVelocity := TVector.Create(0.0, 0.0, 0.0);
    NearPedestriansList := Pedestrian.GetNearPedestrians(aRadius, true, 0.5);
    if NearPedestriansList.Count <> 0 then begin
        for I := 0 to NearPedestriansList.Count - 1 do begin
            Direction := TPedestrianEditor(NearPedestriansList[I]).Destination.Subtract(TPedestrianEditor(NearPedestriansList[I]).Position);
            LocalDirection := Pedestrian.LocalSpace.LocalizeDirection(Direction);
            Direction.Free;
            if LocalDirection.X > 0 then
                DesiredVelocity.AddSelf(TPedestrianEditor(NearPedestriansList[I]).Velocity);
            LocalDirection.Free;
        end;
        DesiredVelocity.DivideSelf(NearPedestriansList.Count);
        DesiredVelocity.SubtractSelf(Pedestrian.LocalSpace.Forward);
    end;
    NearPedestriansList.Free;
    Result := DesiredVelocity;
end;

constructor TCohesion.Create(paRadius: TFloat);
begin
    inherited Create;
    aRadius := paRadius;
end;

constructor TCohesion.CreateCopy(paCohesion: TCohesion);
begin
    inherited CreateCopy(paCohesion);
    aRadius := paCohesion.Radius;
end;

destructor TCohesion.Destroy;
begin
    inherited Destroy;
end;

function TCohesion.Calculate(paPedestrian: Pointer): TVector;
var Pedestrian: TPedestrianEditor;
    DesiredVelocity, Direction, LocalDirection: TVector;
    NearPedestriansList: TPedestriansList;
    I: Integer;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    DesiredVelocity := TVector.Create(0.0, 0.0, 0.0);
    NearPedestriansList := Pedestrian.GetNearPedestrians(aRadius, true, 0.5);
    if NearPedestriansList.Count <> 0 then begin
        for I := 0 to NearPedestriansList.Count - 1 do begin
            Direction := TPedestrianEditor(NearPedestriansList[I]).Destination.Subtract(TPedestrianEditor(NearPedestriansList[I]).Position);
            LocalDirection := Pedestrian.LocalSpace.LocalizeDirection(Direction);
            Direction.Free;
            if LocalDirection.X > 0 then
                DesiredVelocity.AddSelf(TPedestrianEditor(NearPedestriansList[I]).Position);
            LocalDirection.Free;
        end;
        DesiredVelocity.DivideSelf(NearPedestriansList.Count);
        DesiredVelocity.SubtractSelf(Pedestrian.Position);
        DesiredVelocity.NormalizeSelf;
    end;
    NearPedestriansList.Free;
    Result := DesiredVelocity;
end;

constructor TObstacleAvoidance.Create(paRadius: TFloat);
begin
    inherited Create;
    aRadius := paRadius;
end;

constructor TObstacleAvoidance.CreateCopy(paObstacleAvoidance: TObstacleAvoidance);
begin
    inherited CreateCopy(paObstacleAvoidance);
    aRadius := paObstacleAvoidance.Radius;
end;

destructor TObstacleAvoidance.Destroy;
begin
    inherited Destroy;
end;

function TObstacleAvoidance.Calculate(paPedestrian: Pointer): TVector;
var Pedestrian: TPedestrianEditor;
    NearObstacleList: TObstaclesList;
    Repulsive, Temp: TVector;
    Distance, MinimumDistance: TFloat;
    I: Integer;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    Repulsive := TVector.Create;
    MinimumDistance := MaxFloat;
    NearObstacleList := Pedestrian.GetNearObstacles(aRadius, false, 0.5);
    if NearObstacleList.Count <> 0 then begin
        for I := 0 to NearObstacleList.Count - 1 do begin
            Temp := TObstacle(NearObstacleList[I]).GetRepulsive(Pedestrian, Distance);
            if Distance < MinimumDistance then begin
                MinimumDistance := Distance;
                Repulsive.MakeCopy(Temp);
            end;
            Temp.Free;
        end;
    end;
    NearObstacleList.Free;
    Result := Repulsive;
end;

constructor TPathFollow.Create;
begin
    inherited Create;
end;

constructor TPathFollow.CreateCopy(paPathFollow: TPathFollow);
begin
    inherited Create;
end;

destructor TPathFollow.Destroy;
begin
    inherited Destroy;
end;

function TPathFollow.Calculate(paPedestrian: Pointer): TVector;
var Pedestrian: TPedestrianEditor;
    DesiredVelocity: TVector;
begin
    Pedestrian := TPedestrianEditor(paPedestrian);
    if (Pedestrian.Path <> nil) and (Pedestrian.Path <> nil) and (Pedestrian.Path.Count <> 0) then begin
        if Pedestrian.PathCounter = -1 then begin
            if Pedestrian.PathReverse then Pedestrian.PathCounter := Pedestrian.Path.Count - 1
            else Pedestrian.PathCounter := 0;
        end;
        if Pedestrian.Position.IsEqual(TVector(Pedestrian.Path[Pedestrian.PathCounter]), TPathPoint(Pedestrian.Path[Pedestrian.PathCounter]).Radius) then begin
            if (Pedestrian.PathReverse) and (Pedestrian.PathCounter > 0) then
                Pedestrian.PathCounter := Pedestrian.PathCounter - 1
            else if (not Pedestrian.PathReverse) and (Pedestrian.PathCounter < Pedestrian.Path.Count - 1)
                then Pedestrian.PathCounter := Pedestrian.PathCounter + 1;
        end;
        DesiredVelocity := TVector(Pedestrian.Path[Pedestrian.PathCounter]).Subtract(Pedestrian.Position);
        DesiredVelocity.TruncateSelf(Pedestrian.MaxSpeed);
        DesiredVelocity.SubtractSelf(Pedestrian.Velocity);
        Result := DesiredVelocity;
    end
    else Result := TVector.Create;
end;

end.
